#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/stat.h>
#include <iostream>

#include "mysql_net.h"
 #include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/resource.h>
#include <pwd.h>
#include <unistd.h>
#include "log_mgr.h"
#include <getopt.h>
#include <errno.h>
#include "config.h"
#include "my_sql_parse.h"

using namespace std;

CMysqlNet g_sMysqlNet;
std::string g_sBindIp;
uint32_t 	g_dwBindPort;

std::string g_sUser = "mask";
std::string g_sLogPath = "./log";
std::string g_sSharePath = "./sql/";
std::string g_sConfig = "./mask.conf";
Config g_stConfig;
bool g_bDebug = false;
int	g_dwMemPoolSize = 2*1024; //2G
int	g_dwMemSliceSize = 16; //16K
CSqlParser g_sqlParser;


void InitDaemon()
{
    pid_t pid;
    
    if ((pid = fork()) != 0)
        exit(0);
    
    setpgid(0, 0);
    
    signal(SIGINT,  SIG_IGN);
    signal(SIGHUP,  SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
    signal(SIGPIPE, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGCHLD, SIG_IGN);
    
    if ((pid = fork()) != 0)
        exit(0);
    
    umask(0);
	//printf("new priority :%d\n", nice(-19));
}

void CatchSignal(int iSignal)   // Kill -15
{ 
	printf("stop...\n");
    g_sMysqlNet.End();
}

void SetLimit()
{
	struct rlimit r;
	r.rlim_cur=65535;
	r.rlim_max=65535;
	if(setrlimit(RLIMIT_NOFILE, &r)<0)
	{
		printf("setrlimit error, errno:%d\n", errno);
		exit(1);
	}

	if(getrlimit(RLIMIT_NOFILE, &r)<0)
	{
		printf("getrlimit error, errno:%d\n", errno);
		exit(1);
	}
	printf("Get RLIMIT_NOFILE cur:%ld, max:%ld\n", r.rlim_cur, r.rlim_cur);
}

void SetUid()
{
	struct passwd * pwd = getpwnam(g_sUser.c_str());
    if(pwd == NULL)
	{
        printf("getpwnam error, user:%s!, errno:%d\n", g_sUser.c_str(), errno);
		exit(1);
    }

    //set uid
    if(setuid(pwd->pw_uid) != 0)
    {
       printf("setuid to %d error!, errno:%d\n", pwd->pw_uid, errno);
       exit(2);
    }	
}

void Usage()
{
	printf("usage:\n");
	printf("\t--local-ip		: localhost bind ip\n");
	printf("\t--local-port		: localhost bind port\n");
	printf("\t--mysql-data-path	: mysql data file path(charset and errmsg)\n");
	printf("\t--config-file		: config file path\n");
	printf("\t--log-path		: log dir path\n");
	printf("\t--debug			: debug mode\n");
	printf("\t--mempool-size	: number of memory pool size(M)\n");
	printf("\t--memslice-size	: number of memory slice size(K)\n");
}

struct option longopts[] = {
	{"local-ip"		, 1, NULL, 'i'},
	{"local-port"	, 1, NULL, 'p'},
	{"log-path"		, 1, NULL, 'l'},
	{"mysql-data-path"	, 1, NULL, 's'},	
	{"config-file"	, 1, NULL, 'c'},	
	{"debug"		, 2, NULL, 'd'},	
	{"mempool-size"	, 2, NULL, 'P'},	
	{"memslice-size", 2, NULL, 'S'},	
	{0, 0, 0, 0},
};

void GetOpt(int argc, char** argv)
{
	int ch = 0;
	bool bLocalIp = false;
	bool bLocalPort = false;
	bool bSharePath = false;
	int dwMemPoolSize = 0;
	int dwMemSliceSize = 0;
	while((ch = getopt_long(argc, argv, "i:p:s:c:l:dP:S:", longopts, NULL)) != -1)
	{
		switch(ch)
		{
			case 'i':
				printf("bind local ip : %s\n", optarg);
				g_sBindIp.assign(optarg);
				bLocalIp = true;
				break;
			case 'p':
				printf("bind local port : %s\n", optarg);
				g_dwBindPort = atoi(optarg);
				if(g_dwBindPort <= 0 || g_dwBindPort > 65535)
				{
					printf("error local port...\n");
					exit(1);
				}
				bLocalPort = true;
				break;
			case 'l':
				printf("log dir path : %s\n", optarg);
				g_sLogPath.assign(optarg);
				break;
			case 'c':
				printf("config file path : %s\n", optarg);
				g_sConfig.assign(optarg);
				break;
			case 's':
				printf("mysql data file path : %s\n", optarg);
				g_sSharePath.assign(optarg);
				bSharePath = true;
				break;
			case 'd':
				printf("debug mode.\n");
				g_bDebug = true;
				break;
			case 'P':
				printf("memory pool size : %sM\n", optarg);
				dwMemPoolSize = atoi(optarg);
				if(dwMemPoolSize <= 0)
				{
					printf("error memory pool size...\n");
					exit(1);
				}
				break;
			case 'S':
				printf("memory slice size : %sK\n", optarg);
				dwMemSliceSize = atoi(optarg);
				if(dwMemSliceSize <= 0)
				{
					printf("error memory slice size...\n");
					exit(1);
				}
				break;
			case '?':
				printf("Unknown option: %c\n",(char)optopt);
				Usage();
				exit(1);
		}
	}
	
	if(!bLocalIp || !bLocalPort || !bSharePath)
	{
		Usage();
		exit(1);
	}

	if(dwMemPoolSize+dwMemSliceSize > 0)
	{
		if(dwMemPoolSize*1024*16 <= dwMemSliceSize)
		{
			printf("slice size too long, pool:%dM, slice:%dK\n", dwMemPoolSize, dwMemSliceSize);
			exit(1);
		}
		g_dwMemPoolSize = dwMemPoolSize;
		g_dwMemSliceSize = dwMemSliceSize;
	}
	printf("memory pool size:%dM, memory slice size:%dK\n", g_dwMemPoolSize, g_dwMemSliceSize);
}

int main(int argc, char** argv)
{
	GetOpt(argc, argv);
	
	InitDaemon();

	//SetUid();

	SetLimit();

    signal(SIGTERM, CatchSignal);

	//init log
	LOG_MGR->Init();
	
	//start mysql net;
	if(g_sMysqlNet.Start() != 0)
	{
		printf("maskd start error!\n");
		exit(1);
	}

	g_sMysqlNet.Join();
		
	return 0;
}

